﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Diagnostics;
using System.IO;

namespace CFScript.Tasks
{
    /// <summary>
    /// TODO: Update summary.
    /// </summary>
    public class Copy : Task
    {
        public Copy()
        {
            this.name = "<copy>";
            this.parameterNames = new string[] { "fileFrom", "pathTo", "recursive" };
            this.parameterExpandable = new bool[] { true, true, false };
            this.parameterRequired = new bool[] { true, true, false };
            this.parameters = new Dictionary<string, Parameter>();

            Debug.Assert(this.parameterNames.Length == this.parameterRequired.Length &&
                         this.parameterRequired.Length == this.parameterRequired.Length);
        }

        public override InstructionResult Execute(CFScript context)
        {
            InstructionResult result = new InstructionResult();
            result.ExecutedInstruction = this;

            string fileFrom = this.parameters["fileFrom"].GetValue(context);
            string pathTo = this.parameters["pathTo"].GetValue(context);
            bool recursiveCopy = this.parameters.ContainsKey("recursive") && this.parameters["recursive"].GetValueAsBoolean();
            bool sourceIsADirectory = false;

            try
            {
                // Let's copy the file

                FileInfo sourceFileInfo = new FileInfo(fileFrom);

                sourceIsADirectory = (sourceFileInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory;

                if (sourceFileInfo.Exists)
                {
                    if (sourceIsADirectory)
                    {
                        // It's a directory, so pathToCopy must be a directory
                        FileInfo targetFileInfo = new FileInfo(pathTo);
                        if ((targetFileInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
                        {
                            if (fileFrom != pathTo)
                            {
                                CopyDirectory(fileFrom, pathTo, recursiveCopy);
                            }
                            else
                            {
                                result.Code = ResultCode.ERROR;
                                switch (context.Options.VerboseLevel)
                                {
                                    case VerboseMode.MINIMUM:
                                    case VerboseMode.ALL:
                                        result.Message = string.Format("Attributes 'fileFrom' and 'pathTo' must be different");
                                        break;
                                }
                            }
                        }
                        else
                        {
                            result.Code = ResultCode.ERROR;
                            switch (context.Options.VerboseLevel)
                            {
                                case VerboseMode.MINIMUM:
                                case VerboseMode.ALL:
                                    result.Message = string.Format("Attribute 'fileFrom' references a directory, so attribute 'pathTo' must reference a directory. A file found, instead ({0})", pathTo);
                                    break;
                            }
                        }
                    }
                    else
                    {
                        // It's a single file, so pathToCopy may be a directory or a new name (rename)
                        FileInfo targetFileInfo = new FileInfo(pathTo);
                        if ((targetFileInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
                        {
                            // pathTo is a directory, so copy with the same name
                            string sourceDirectory = Path.GetDirectoryName(fileFrom);
                            if (sourceDirectory != pathTo)
                            {
                                string name = Path.GetFileName(fileFrom);
                                string dest = Path.Combine(pathTo, name);
                                // If pathTo doesn't exist, we'll create
                                if (!Directory.Exists(pathTo))
                                {
                                    Directory.CreateDirectory(pathTo);
                                }
                                File.Copy(fileFrom, dest);
                            }
                            else
                            {
                                result.Code = ResultCode.ERROR;
                                switch (context.Options.VerboseLevel)
                                {
                                    case VerboseMode.MINIMUM:
                                    case VerboseMode.ALL:
                                        result.Message = string.Format("Attribute 'pathTo' is the directory of file referenced by 'fileFrom' attribute.");
                                        break;
                                }
                            }
                        }
                        else
                        {
                            // pathTo is a file, so renaming is coming...
                            if (fileFrom != pathTo)
                            {
                                string destPath = Path.GetDirectoryName(pathTo);
                                // If destPath doesn't exist, we'll create
                                if (!Directory.Exists(destPath))
                                {
                                    Directory.CreateDirectory(destPath);
                                }
                                File.Copy(fileFrom, pathTo);
                            }
                            else
                            {
                                result.Code = ResultCode.ERROR;
                                switch (context.Options.VerboseLevel)
                                {
                                    case VerboseMode.MINIMUM:
                                    case VerboseMode.ALL:
                                        result.Message = string.Format("Attributes 'fileFrom' and 'pathTo' must be different.");
                                        break;
                                }
                            }
                        }
                    }
                    result.Code = ResultCode.OK;
                }
                else
                {
                    // File not present. Can't delete
                    result.Code = ResultCode.WARN;
                    switch (context.Options.VerboseLevel)
                    {
                        case VerboseMode.MINIMUM:
                        case VerboseMode.ALL:
                            result.Message = string.Format("Source {0} {1} not found", sourceIsADirectory?"path":"file",fileFrom);
                            break;
                    }
                }
            }
            catch (Exception ex)
            {
                result.Code = ResultCode.ERROR;
                result.Message = ex.Message;
            }

            result.BreakExecution = result.Code == ResultCode.ERROR && this.options.OnError == OnErrorAction.BREAK;

            return result;
        }

        private void CopyDirectory(string sourceDirectory, string destDirectory, bool recursive)
        {
            if (!Directory.Exists(destDirectory))
            {
                Directory.CreateDirectory(destDirectory);
            }
            string[] files = Directory.GetFiles(sourceDirectory);
            foreach (string file in files)
            {
                string name = Path.GetFileName(file);
                string dest = Path.Combine(destDirectory, name);
                File.Copy(file, dest);
            }

            if (recursive)
            {
                string[] folders = Directory.GetDirectories(sourceDirectory);
                foreach (string folder in folders)
                {
                    string name = Path.GetFileName(folder);
                    string dest = Path.Combine(destDirectory, name);
                    CopyDirectory(folder, dest, recursive);
                }
            }
        }
    }
}
